/*! 
 * Implementation file for a wrapper class that holds fast intersection tests.
 *
 * \author Devis Rossini
 * \date 19 March 2012
 */
#include "PhxIntersections.h"

#include "PhxCore.h"

using namespace phx;

bool IntersectionTests::SphereAndSphere(const CollisionSphere& one, const CollisionSphere& two)
{
	// Find the vector between the objects
	Vector3 midline = one.GetAxis(3) - two.GetAxis(3);

	// See if it is large enough.
	return midline.SquareMagnitude() <
		(one.mRadius + two.mRadius) * (one.mRadius + two.mRadius);
}

static inline real TransformToAxis(const CollisionBox& box, const Vector3& axis)
{
	return
		box.mHalfSize.x * real_abs(axis * box.GetAxis(0)) +
		box.mHalfSize.y * real_abs(axis * box.GetAxis(1)) +
		box.mHalfSize.z * real_abs(axis * box.GetAxis(2));
}

static inline bool OverlapOnAxis(const CollisionBox& one, const CollisionBox& two, const Vector3& axis, const Vector3& toCentre)
{
    // Project the half-size of one onto axis
    real oneProject = TransformToAxis(one, axis);
    real twoProject = TransformToAxis(two, axis);

    // Project this onto the axis
    real distance = real_abs(toCentre * axis);

    // Check for overlap
    return (distance < oneProject + twoProject);
}

bool IntersectionTests::BoxAndBox(const CollisionBox& one, const CollisionBox& two)
{
    // Find the vector between the two centres
    Vector3 toCentre = two.GetAxis(3) - one.GetAxis(3);

    return (
        // Check on box one's axes first
        OverlapOnAxis(one, two, one.GetAxis(0), toCentre) &&
        OverlapOnAxis(one, two, one.GetAxis(1), toCentre) &&
        OverlapOnAxis(one, two, one.GetAxis(2), toCentre) &&

        // And on two's
        OverlapOnAxis(one, two, two.GetAxis(0), toCentre) &&
        OverlapOnAxis(one, two, two.GetAxis(1), toCentre) &&
        OverlapOnAxis(one, two, two.GetAxis(2), toCentre) &&

        // Now on the cross products
        OverlapOnAxis(one, two, one.GetAxis(0) % two.GetAxis(0), toCentre) &&
		OverlapOnAxis(one, two, one.GetAxis(0) % two.GetAxis(1), toCentre) &&
        OverlapOnAxis(one, two, one.GetAxis(0) % two.GetAxis(2), toCentre) &&
        OverlapOnAxis(one, two, one.GetAxis(1) % two.GetAxis(0), toCentre) &&
        OverlapOnAxis(one, two, one.GetAxis(1) % two.GetAxis(1), toCentre) &&
        OverlapOnAxis(one, two, one.GetAxis(1) % two.GetAxis(2), toCentre) &&
        OverlapOnAxis(one, two, one.GetAxis(2) % two.GetAxis(0), toCentre) &&
        OverlapOnAxis(one, two, one.GetAxis(2) % two.GetAxis(1), toCentre) &&
        OverlapOnAxis(one, two, one.GetAxis(2) % two.GetAxis(2), toCentre)
    );
}

bool IntersectionTests::SphereAndPlane(const CollisionSphere& sphere, const CollisionPlane& plane)
{
	// Find the distance from the origin
	real ballDistance = plane.mDirection * sphere.GetAxis(3) - sphere.mRadius;

	// Check for the intersection
	return ballDistance <= plane.mOffset;
}

bool IntersectionTests::BoxAndPlane(const CollisionBox& box, const CollisionPlane& plane)
{
	// Work out the projected radius of the box onto the plane direction
	real projectedRadius = TransformToAxis(box, plane.mDirection);

	// Work out how far the box is from the origin
	real boxDistance = plane.mDirection * box.GetAxis(3) - projectedRadius;

	// Check for the intersection
	return boxDistance <= plane.mOffset;
}